/*
 * Copyright 2011 Clean GWT
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package com.cleanform.gwt.chinesechess.shared.model;

import java.util.ArrayList;
import java.util.List;

/**
 * The chessboard state.
 * 
 * 
 * @author Jess
 * @version 1.0
 * @since 1.0
 */
public class ChessboardState {
	public static final int HSize = 9;
	public static final int VSize = 10;
	public static final int HBound = 8;
	public static final int VBound = 9;

	public int pieces[][] = new int[10][9];

	public ChessboardState() {
		reset();
	}

	public ChessboardState(int chessboard[][]) {
		pieces = chessboard;
	}

	public void applyMove(ChessMove move) {
		pieces[move.getTy()][move.getTx()] = pieces[move.getSy()][move.getSx()];
		pieces[move.getSy()][move.getSx()] = Chess.EMPTY;
	}

	public List<ChessMove> getAvalibleMoves(int sx, int sy) {
		if (isEmpty(sx, sy)) {
			return null;
		}

		List<ChessMove> moves = new ArrayList<ChessMove>();

		boolean reverse = pieces[sy][sx] < 0;

		switch (Math.abs(pieces[sy][sx])) {
		case Chess.KING:
			// check boundary
			int k_vlbound;
			int k_vubound;
			if (reverse) {
				k_vlbound = 7;
				k_vubound = 9;
			} else {
				k_vlbound = 0;
				k_vubound = 2;
			}
			// test 4 moves
			int k_xs[] = { sx, sx, sx - 1, sx + 1 };
			int k_ys[] = { sy - 1, sy + 1, sy, sy };
			for (int i = 0; i < 4; i++) {
				// in boundary
				if (k_xs[i] >= 3 && k_xs[i] <= 5 && k_ys[i] >= k_vlbound && k_ys[i] <= k_vubound) {
					// there is no-chess or opponent
					if (isOppositiveOrEmpty(sx, sy, k_xs[i], k_ys[i])) {

						moves.add(getChessMove(sx, sy, k_xs[i], k_ys[i]));
					}
				}
			}
			return moves;
		case Chess.ROOK:
			// horizontal move
			for (int x = 0; x <= HBound; x++) {
				if (x == sx) {
					continue;
				}
				if (isOppositiveOrEmpty(sx, sy, x, sy)) {
					// check block
					boolean bolcked = false;
					int inc = sx > x ? -1 : 1;
					for (int s = sx + inc; s != x; s += inc) {
						if (isChess(s, sy)) {
							bolcked = true;
							break;
						}
					}
					if (!bolcked) {
						moves.add(getChessMove(sx, sy, x, sy));
					}
				}
			}
			// vertical move
			for (int y = 0; y < VSize; y++) {
				if (y == sy) {
					continue;
				}
				if (isOppositiveOrEmpty(sx, sy, sx, y)) {
					// check block
					boolean bolcked = false;
					int inc = sy > y ? -1 : 1;
					for (int s = sy + inc; s != y; s += inc) {
						if (isChess(sx, s)) {
							bolcked = true;
							break;
						}
					}
					if (!bolcked) {
						moves.add(getChessMove(sx, sy, sx, y));
					}
				}
			}
			return moves;
		case Chess.HORSE:
			// test 8 moves

			int xs[] = { sx - 1, sx - 1, sx - 2, sx - 2, sx + 1, sx + 1, sx + 2, sx + 2 };
			int ys[] = { sy - 2, sy + 2, sy - 1, sy + 1, sy - 2, sy + 2, sy - 1, sy + 1 };
			int xblocks[] = { sx, sx, sx - 1, sx - 1, sx, sx, sx + 1, sx + 1 };
			int yblocks[] = { sy - 1, sy + 1, sy, sy, sy - 1, sy + 1, sy, sy };
			for (int i = 0; i < 8; i++) {
				// in boundary
				if (xs[i] >= 0 && xs[i] < HSize && ys[i] >= 0 && ys[i] < VSize) {
					if (isOppositiveOrEmpty(sx, sy, xs[i], ys[i])) {
						// no block
						if (isEmpty(xblocks[i], yblocks[i])) {
							moves.add(getChessMove(sx, sy, xs[i], ys[i]));
						}
					}
				}
			}

			return moves;
		case Chess.CANNON:
			// horizontal move
			for (int x = 0; x <= HBound; x++) {
				if (x == sx) {
					continue;
				}
				int blocks = 0;
				int inc = sx > x ? -1 : 1;
				for (int s = sx + inc; s != x; s += inc) {
					if (isChess(s, sy)) {
						blocks++;
					}
				}
				if (blocks == 0 && isEmpty(x, sy)) {
					moves.add(getChessMove(sx, sy, x, sy));
				} else if (blocks == 1 && isOppositive(sx, sy, x, sy)) {
					moves.add(getChessMove(sx, sy, x, sy));
				}

			}

			// vertical move
			for (int y = 0; y <= VBound; y++) {
				if (y == sy) {
					continue;
				}
				int blocks = 0;
				int inc = sy > y ? -1 : 1;
				for (int s = sy + inc; s != y; s += inc) {
					if (isChess(sx, s)) {
						blocks++;
					}
				}
				if (blocks == 0 && isEmpty(sx, y)) {
					moves.add(getChessMove(sx, sy, sx, y));
				} else if (blocks == 1 && isOppositive(sx, sy, sx, y)) {
					moves.add(getChessMove(sx, sy, sx, y));
				}
			}
			return moves;
		case Chess.ELEPHANT:
			// check boundary
			int e_vlbound;
			int e_vubound;
			if (reverse) {
				e_vlbound = 5;
				e_vubound = 9;
			} else {
				e_vlbound = 0;
				e_vubound = 4;
			}
			// test 4 moves
			int e_xs[] = { sx - 2, sx - 2, sx + 2, sx + 2 };
			int e_ys[] = { sy - 2, sy + 2, sy - 2, sy + 2 };
			int e_xblocks[] = { sx - 1, sx - 1, sx + 1, sx + 1 };
			int e_yblocks[] = { sy - 1, sy + 1, sy - 1, sy + 1 };
			for (int i = 0; i < 4; i++) {
				// in boundary
				if (e_xs[i] >= 0 && e_xs[i] <= HBound && e_ys[i] >= e_vlbound && e_ys[i] <= e_vubound) {
					if (isOppositiveOrEmpty(sx, sy, e_xs[i], e_ys[i])) {
						// no block
						if (isEmpty(e_xblocks[i], e_yblocks[i])) {
							moves.add(getChessMove(sx, sy, e_xs[i], e_ys[i]));
						}
					}
				}
			}
			return moves;
		case Chess.ADVISOR:
			// check boundary
			int a_vlbound;
			int a_middle;
			int a_vubound;
			if (reverse) {
				a_vlbound = 7;
				a_middle = 8;
				a_vubound = 9;

			} else {
				a_vlbound = 0;
				a_middle = 1;
				a_vubound = 2;
			}
			if (sx == 4 && sy == a_middle) {
				if (isOppositiveOrEmpty(sx, sy, 3, a_vubound)) {
					moves.add(getChessMove(sx, sy, 3, a_vubound));
				}
				if (isOppositiveOrEmpty(sx, sy, 5, a_vubound)) {
					moves.add(getChessMove(sx, sy, 5, a_vubound));
				}
				if (isOppositiveOrEmpty(sx, sy, 3, a_vlbound)) {
					moves.add(getChessMove(sx, sy, 3, a_vlbound));
				}
				if (isOppositiveOrEmpty(sx, sy, 5, a_vlbound)) {
					moves.add(getChessMove(sx, sy, 5, a_vlbound));
				}
			} else {
				if (isOppositiveOrEmpty(sx, sy, 4, 8)) {
					moves.add(getChessMove(sx, sy, 4, a_middle));
				}
			}

			return moves;
		case Chess.PAWN:

			if ((reverse && sy <= 4) || (!reverse && sy >= 5)) {
				// if pass river test 3 moves
				int p_xs[] = { sx - 1, sx, sx + 1 };
				int p_ys[] = { sy, sy + (reverse ? -1 : 1), sy };
				for (int i = 0; i < 3; i++) {
					// in boundary
					if (p_xs[i] >= 0 && p_xs[i] <= HBound && p_ys[i] >= 0 && p_ys[i] <= VBound) {
						if (isOppositiveOrEmpty(sx, sy, p_xs[i], p_ys[i])) {
							moves.add(getChessMove(sx, sy, p_xs[i], p_ys[i]));
						}
					}
				}
			} else {
				// test one move
				int p_y = sy + (reverse ? -1 : 1);
				// in boundary
				if (p_y >= 0 && p_y <= VBound) {
					if (isOppositiveOrEmpty(sx, sy, sx, p_y)) {
						moves.add(getChessMove(sx, sy, sx, p_y));
					}
				}

			}
			return moves;

		}
		return moves;
	}

	public ChessMove[][][] getAvalibleMoves3(int active) {
		ChessMove[][][] moves = new ChessMove[2][7][];
		int c[][] = new int[2][7];

		moves[0][0] = new ChessMove[8];
		moves[0][1] = new ChessMove[16];
		moves[0][2] = new ChessMove[8];
		moves[0][3] = new ChessMove[6];
		moves[0][4] = new ChessMove[4];
		moves[0][5] = new ChessMove[15];
		moves[0][6] = new ChessMove[4];
		//
		moves[1][0] = new ChessMove[34];
		moves[1][1] = new ChessMove[16];
		moves[1][2] = new ChessMove[34];
		moves[1][3] = new ChessMove[6];
		moves[1][4] = new ChessMove[4];
		moves[1][5] = new ChessMove[15];
		moves[1][6] = new ChessMove[4];

		int chessType;
		int force;
		int priority;
		int sx, sy, tx, ty;
		for (sy = 0; sy < VSize; sy++) {
			for (sx = 0; sx < HSize; sx++) {
				if ((force = getForce(sx, sy)) == active) {
					chessType = Math.abs(pieces[sy][sx]);
					switch (chessType) {
					case Chess.KING:
						for (Location move : Chess.KING_MOVES[sy][sx]) {
							if ((priority = getTargetPriority(force, pieces[move.y][move.x])) != -1) {
								moves[priority][6][c[priority][6]++] = getChessMove(sx, sy, move.x, move.y);
							}
						}

						break;
					case Chess.ROOK:
						// horizontal move
						// west move
						for (tx = sx - 1; tx > -1; tx--) {
							priority = getTargetPriority(force, pieces[sy][tx]);
							if (priority != -1) {
								moves[priority][0][c[priority][0]++] = getChessMove(sx, sy, tx, sy);
							}
							if (priority != 1) {
								break;
							}
						}
						// east move
						for (tx = sx + 1; tx < HSize; tx++) {
							priority = getTargetPriority(force, pieces[sy][tx]);
							if (priority != -1) {
								moves[priority][0][c[priority][0]++] = getChessMove(sx, sy, tx, sy);
							}
							if (priority != 1) {
								break;
							}
						}

						// south move
						for (ty = sy - 1; ty > -1; ty--) {
							priority = getTargetPriority(force, pieces[ty][sx]);
							if (priority != -1) {
								moves[priority][0][c[priority][0]++] = getChessMove(sx, sy, sx, ty);
							}
							if (priority != 1) {
								break;
							}
						}
						// north move
						for (ty = sy + 1; ty < VSize; ty++) {
							priority = getTargetPriority(force, pieces[ty][sx]);
							if (priority != -1) {
								moves[priority][0][c[priority][0]++] = getChessMove(sx, sy, sx, ty);
							}
							if (priority != 1) {
								break;
							}
						}

						break;
					case Chess.HORSE:
						for (int i = 0; i < 8; i++) {
							tx = sx + Chess.HORSE_HMOVES[i];
							ty = sy + Chess.HORSE_VMOVES[i];
							if (tx >= 0 && tx < HSize && ty >= 0 && ty < VSize) {
								if (isEmpty(sx + Chess.HORSE_HBLOCKS[i], sy + Chess.HORSE_VBLOCKS[i])) {
									priority = getTargetPriority(force, pieces[ty][tx]);
									if (priority != -1) {
										moves[priority][1][c[priority][1]++] = getChessMove(sx, sy, tx, ty);
									}

								}

							}
						}
						break;
					case Chess.CANNON:
						boolean oneBlock = false;
						// west move
						for (tx = sx - 1; tx > -1; tx--) {
							priority = getTargetPriority(force, pieces[sy][tx]);
							if (oneBlock) {
								if (priority == 0) {// opposite
									moves[priority][2][c[priority][2]++] = getChessMove(sx, sy, tx, sy);
									break;
								} else if (priority == -1) {
									break;
								}
							} else {
								if (priority == 1) {// empty
									moves[priority][2][c[priority][2]++] = getChessMove(sx, sy, tx, sy);
								} else {
									oneBlock = true;
								}
							}
						}
						oneBlock = false;
						// east move
						for (tx = sx + 1; tx < HSize; tx++) {
							priority = getTargetPriority(force, pieces[sy][tx]);
							if (oneBlock) {
								if (priority == 0) {// opposite
									moves[priority][2][c[priority][2]++] = getChessMove(sx, sy, tx, sy);
									break;
								} else if (priority == -1) {// self-forc
									break;
								}
							} else {
								if (priority == 1) {// empty
									moves[priority][2][c[priority][2]++] = getChessMove(sx, sy, tx, sy);
								} else {
									oneBlock = true;
								}
							}
						}
						oneBlock = false;
						// north move
						for (ty = sy - 1; ty > -1; ty--) {
							priority = getTargetPriority(force, pieces[ty][sx]);
							if (oneBlock) {
								if (priority == 0) {// opposite
									moves[priority][2][c[priority][2]++] = getChessMove(sx, sy, sx, ty);
									break;
								} else if (priority == -1) {// self-forc
									break;
								}
							} else {
								if (priority == 1) {// empty
									moves[priority][2][c[priority][2]++] = getChessMove(sx, sy, sx, ty);
								} else {
									oneBlock = true;
								}
							}

						}
						oneBlock = false;
						// south move
						for (ty = sy + 1; ty < VSize; ty++) {
							priority = getTargetPriority(force, pieces[ty][sx]);
							if (oneBlock) {

								if (priority == 0) {// opposite
									moves[priority][2][c[priority][2]++] = getChessMove(sx, sy, sx, ty);
									break;
								} else if (priority == -1) {// self-forc
									break;
								}
							} else {
								if (priority == 1) {// empty
									moves[priority][2][c[priority][2]++] = getChessMove(sx, sy, sx, ty);
								} else {
									oneBlock = true;
								}
							}
						}
						break;
					case Chess.ELEPHANT:
						int k = 0;
						for (Location move : Chess.ELEMPHAT_MOVES[sy][sx]) {
							if (isEmpty(Chess.ELEMPHAT_BLOCKS[sy][sx][k].x, Chess.ELEMPHAT_BLOCKS[sy][sx][k].y)) {
								if ((priority = getTargetPriority(force, pieces[move.y][move.x])) != -1) {
									moves[priority][3][c[priority][3]++] = getChessMove(sx, sy, move.x, move.y);
								}
							}
							k++;
						}
						break;
					case Chess.ADVISOR:
						for (Location move : Chess.ADVISOR_MOVES[sy][sx]) {
							if ((priority = getTargetPriority(force, pieces[move.y][move.x])) != -1) {
								moves[priority][4][c[priority][4]++] = getChessMove(sx, sy, move.x, move.y);
							}
						}
						break;
					case Chess.PAWN:
						boolean forwardable;
						boolean passRiver;
						int forward;
						if (force == Chess.FORCE_BLACK) {
							forwardable = sy > 0;
							passRiver = sy < 5;
							forward = sy - 1;
						} else {
							forwardable = sy < VBound;
							passRiver = sy > 4;
							forward = sy + 1;
						}
						// forward move
						if (forwardable) {
							priority = getTargetPriority(force, pieces[forward][sx]);
							if (priority != -1) {
								moves[priority][5][c[priority][5]++] = getChessMove(sx, sy, sx, forward);
							}
						}

						if (passRiver) {
							// west move
							if (sx > 0) {
								tx = sx - 1;
								if ((priority = getTargetPriority(force, pieces[sy][tx])) != -1) {
									moves[priority][5][c[priority][5]++] = getChessMove(sx, sy, tx, sy);
								}
							}
							// east move
							if (sx < HBound) {
								tx = sx + 1;
								if ((priority = getTargetPriority(force, pieces[sy][tx])) != -1) {
									moves[priority][5][c[priority][5]++] = getChessMove(sx, sy, tx, sy);
								}
							}

						}

					}
				}
			}
		}

		return moves;
	}

	public int getChess(int x, int y) {
		return pieces[y][x];
	}

	public ChessMove getChessMove(int sx, int sy, int tx, int ty) {
		return new ChessMove(sx, sy, tx, ty, pieces[sy][sx], pieces[ty][tx]);
	}

	public int getForce(int sx, int sy) {
		return pieces[sy][sx] == 0 ? 0 : pieces[sy][sx] > 0 ? 1 : -1;
	}

	public int[][] getPieces() {
		return pieces;
	}

	public boolean isChess(int x, int y) {
		return pieces[y][x] != Chess.EMPTY;
	}

	public boolean isEmpty(int x, int y) {
		return pieces[y][x] == Chess.EMPTY;
	}

	public boolean isOppositive(int sx, int sy, int tx, int ty) {
		return pieces[sy][sx] * pieces[ty][tx] < 0;
	}

	public boolean isOppositiveOrEmpty(int sx, int sy, int tx, int ty) {
		return pieces[ty][tx] == Chess.EMPTY || pieces[sy][sx] * pieces[ty][tx] < 0;
	}

	public void reset() {
		pieces = new int[][] { { 2, 3, 5, 6, 1, 6, 5, 3, 2 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0 },
				{ 0, 4, 0, 0, 0, 0, 0, 4, 0 }, { 7, 0, 7, 0, 7, 0, 7, 0, 7 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0 },
				{ 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { -7, 0, -7, 0, -7, 0, -7, 0, -7 }, { 0, -4, 0, 0, 0, 0, 0, -4, 0 },
				{ 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { -2, -3, -5, -6, -1, -6, -5, -3, -2 } };
	}

	/**
	 * 
	 */
	public void undoMove(ChessMove move) {
		pieces[move.getSy()][move.getSx()] = move.getSource();
		pieces[move.getTy()][move.getTx()] = move.getTarget();
	}

	/**
	 * 
	 * @param force
	 * @param target
	 * @return 1 as if target is empty , -1 as same force , 0 as opposite.
	 */
	private int getTargetPriority(int force, int target) {
		return target == Chess.EMPTY ? 1 : ((force == 1 && target > 0) || (force == -1 && target < 0)) ? -1 : 0;
	}

}
